#ifndef GAMEENGINE_GRAPHICS_CAMERA_CAMERA_MANIPULATOR_H
#define GAMEENGINE_GRAPHICS_CAMERA_CAMERA_MANIPULATOR_H

#include <GameEngine/Graphics/graphics_settings.h>

#include <GameEngine/Graphics/Camera/camera.h>
#include <GameEngine/Geometry/Location/vector2d.h>

#include <QtGui/QKeySequence>
#include <QtCore/QMap>
#include <QtCore/QPair>
#include <QtCore/QObject>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//! A camera manipulator defines how mouse events affect the view point
/*!
 *  The camera manipulator is responsible for handling how the player can interact
 *  with the view point in order to see a scene from a closer view, a different
 *  angle, etc. These allowed manipulations are encoded as a set of bindings between
 *  the mouse button that has been pressed and the effect it should have on the
 *  camera (defining the actual effect is deferred to derived classes).
 *
 *  The camera manipulator class is currently unable to handle "unexpected" types of
 *  events, such as key strokes, mouse moved in a specific area of the screen, etc.
 *  One way to work around this issue is to defined such events in derived classes in
 *  parallel to mouse press events, but this way is not very practical.
 *  Another workaround is to manually call the manipulate() function and supply one
 *  of the mouse buttons as an argument of the function to trigger the effect to which
 *  it was bound (even though this button was not physically pressed). Besides not
 *  being very practical, this way is also not very intuitive...
 *
 @todo These limitations are expected to be released in future versions of GameEngine.<br>
       Future versions will implement more events than simply buttons pressed, in order to
       natively allow different interactions
 */
class _Graphics_decl CameraManipulator : public QObject {
	Q_OBJECT

public:
	//! Effect of a key sequence on the camera manipulator
	enum CameraEffect {
		//! Zoom to the target
		EffectZoom,
		//! Pan the view
		EffectPan,
		//! Rotate the view
		EffectRotate,
		//! No effect
		EffectNone
	};
	//! Mouse button
	enum MouseButton {
		//! No mouse button
		MouseNone,
		//! Left mouse button
		MouseLeft,
		//! Middle mouse button
		MouseMiddle,
		//! Right mouse button
		MouseRight
	};

	CameraManipulator();
	CameraManipulator(const CameraManipulator& rhs);
	CameraManipulator& operator=(const CameraManipulator& rhs);
	virtual ~CameraManipulator();

	const Camera& camera() const;
	Camera& camera();

	virtual bool add_manipulation(MouseButton button, CameraEffect effect, bool multiple_allowed = false);
	virtual int remove_manipulation(MouseButton button, CameraEffect effect);
	virtual QList<CameraEffect> find_manipulation(MouseButton button) const;
	virtual bool find_manipulation(MouseButton button, CameraEffect effect) const;

	float zoom_speed() const;
	float& zoom_speed();
	float pan_speed() const;
	float& pan_speed();
	float rotation_speed() const;
	float& rotation_speed();

	virtual bool update();

	//! Apply the given manipulation to the camera
	/*!
	 @note Whenever the camera has been manipulated, this function should emit the
	       manipulated() signal. Do not forget to include this feature in derived
		   classes when implementing this function
	 @param button The mouse button that was pressed
	 @param mouse_move The total amout of displacement of the mouse on the screen
	 @return True if given manipulation could be applied to the camera, false otherwise
	 */
	virtual bool manipulate(MouseButton button, const Vector2d<int>& mouse_move) = 0;

signals:
	//! Signal emited when the camera is manipulated (i.e., the view point changes)
	void manipulated() const;

protected:
	//! Camera associated to the manipulator
	Camera camera_;

	//! Maps mouse buttons to camera manipulations
	QList< QPair<MouseButton, CameraEffect> > bindings_;

	//! Zoom speed
	float zoom_speed_;
	//! Pan speed
	float pan_speed_;
	//! Rotation speed
	float rotation_speed_;
};

//========================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
